(function ( exports ) {

    //region AttributeMetadata

    function AttributeMetadata () {
        COSA.MappableNode.call(this);
    }

    COSA.Util.deriveOverride(COSA.MappableNode, AttributeMetadata, {
        getMap: function () {
            return (new COSA.ExtensibleObject((this.getBase()).getMap()))
                .extendWith(this.getBaseMapExtensions())
                .result();
        },
        getBaseMapExtensions: function () {
            return {
                'metadataId': 'string:MetadataId',
                'hasChanged': 'boolean:HasChanged',
                'attributeOf': 'string:AttributeOf',
                'attributeType': 'string:AttributeType',
                'canBeSecuredForCreate': 'boolean:CanBeSecuredForCreate',
                'canBeSecuredForRead': 'boolean:CanBeSecuredForUpdate',
                'canBeSecuredForUpdate': 'boolean:CanBeSecuredForUpdate',
                'canModifyAdditionalSettings': 'managedProperty:CanModifyAdditionalSettings',
                'columnNumber': 'int:ColumnNumber',
                'deprecatedVersion': 'string:DeprecatedVersion',
                'description': 'localizedLabelSet:Description',
                'displayName': 'localizedLabelSet:DisplayName',
                'entityLogicalName': 'string:EntityLogicalName',
                'isAuditEnabled': 'managedProperty:IsAuditEnabled',
                'isCustomAttribute': 'boolean:IsCustomAttribute',
                'isCustomizable': 'managedProperty:IsCustomizable',
                'isManaged': 'boolean:IsManaged',
                'isPrimaryId': 'boolean:IsPrimaryId',
                'isPrimaryName': 'boolean:IsPrimaryName',
                'isRenameable': 'managedProperty:IsRenameable',
                'isSecured': 'boolean:IsSecured',
                'isValidForAdvancedFind': 'managedProperty:IsValidForAdvancedFind',
                'isValidForCreate': 'boolean:IsValidForCreate',
                'isValidForRead': 'boolean:IsValidForRead',
                'isValidForUpdate': 'boolean:IsValidForUpdate',
                'linkedAttributeId': 'string:LinkedAttributeId',
                'logicalName': 'string:LogicalName',
                'requiredLevel': 'managedProperty:RequiredLevel',
                'schemaName': 'string:SchemaName',
                'attributeTypeName': 'valueNode:AttributeTypeName',
                'introducedVersion': 'string:IntroducedVersion',
                'isLogical': 'boolean:IsLogical',
                'sourceType': 'int:SourceType'
            };
        }
    });

    //endregion AttributeMetadata

    //region StringAttributeMetadata

    function StringAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, StringAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'sourceTypeMask':'int:SourceTypeMask',
                'isLocalizable': 'boolean:IsLocalizable',
                'formulaDefinition': 'string:FormulaDefinition',
                'formatName': 'valueNode:FormatName',
                'yomiOf':'string:YomiOf',
                'maxLength':'int:MaxLength',
                'imeMode':'string:ImeMode',
                'format':'string:Format'
            };
        }
    });

    //endregion StringAttributeMetadata

    //region LookupAttributeMetadata

    function LookupAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, LookupAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'targets':'stringCollection:Targets'
            };
        }
    });

    //endregion LookupAttributeMetadata

    //region OptionSetAttributeMetadata

    function OptionSetAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, OptionSetAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'optionSet':'optionSetMetadata:OptionSet',
                'formulaDefinition':'string:FormulaDefinition',
                'sourceTypeMask':'int:SourceTypeMask'
            };
        }
    });

    //endregion OptionSetAttributeMetadata

    //region PicklistAttributeMetadata

    function PicklistAttributeMetadata () {
        OptionSetAttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(OptionSetAttributeMetadata, PicklistAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'defaultFormValue':'int:DefaultFormValue'
            };
        }
    });

    //endregion PicklistAttributeMetadata

    //region BooleanAttributeMetadata

    function BooleanAttributeMetadata () {
        OptionSetAttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(OptionSetAttributeMetadata, BooleanAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'optionSet':'boolOptionSetMetadata:OptionSet'
            };
        }
    });

    //endregion BooleanAttributeMetadata

    //region DateTimeAttributeMetadata

    function DateTimeAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, DateTimeAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'format': 'string:Format',
                'imeMode': 'string:ImeMode',
                'formulaDefinition':'string:FormulaDefinition',
                'sourceTypeMask':'int:SourceTypeMask'
            };
        }
    });

    //endregion DateTimeAttributeMetadata

    //region MemoAttributeMetadata

    function MemoAttributeMetadata ( ) {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, MemoAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'format':'string:Format',
                'imeMode':'string:ImeMode',
                'maxLength':'int:MaxLength',
                'isLocalizable':'boolean:IsLocalizable'
            };
        }
    });

    //endregion MemoAttributeMetadata

    //region ImageAttributeMetadata

    function ImageAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, ImageAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'isPrimaryImage':'boolean:IsPrimaryImage',
                'maxHeight':'int:MaxHeight',
                'maxWidth':'int:MaxWidth'
            };
        }
    });

    //endregion ImageAttributeMetadata

    //region IntegerAttributeMetadata

    function IntegerAttributeMetadata() {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, IntegerAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'format':'string:Format',
                'maxValue':'int:MaxValue',
                'minValue':'int:MinValue',
                'formulaDefinition':'string:FormulaDefinition',
                'sourceTypeMask':'int:SourceTypeMask'
            };
        }
    });

    //endregion IntegerAttributeMetadata

    //region DoubleAttributeMetadata

    function DoubleAttributeMetadata () {
        AttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(AttributeMetadata, DoubleAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'imeMode':'string:ImeMode',
                'maxValue':'int:MaxValue',
                'minValue':'int:MinValue',
                'precision':'int:Precision'
            };
        }
    });

    //endregion DoubleAttributeMetadata

    //region DecimalAttributeMetadata

    function DecimalAttributeMetadata () {
        DoubleAttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(DoubleAttributeMetadata, DecimalAttributeMetadata, {
        getBaseMapExtensions: function ( ) {
            return {
                'formulaDefinition':'string:FormulaDefinition',
                'sourceTypeMask':'int:SourceTypeMask'
            };
        }
    });

    //endregion DecimalAttributeMetadata

    //region MoneyAttributeMetadata

    function MoneyAttributeMetadata () {
        DecimalAttributeMetadata.call(this);
    }

    COSA.Util.deriveOverride(DecimalAttributeMetadata, MoneyAttributeMetadata, {
        getBaseMapExtensions: function () {
            return {
                'calculationOf':'string:CalculationOf',
                'precisionSource':'int:PrecisionSource',
                'isBaseCurrency':'boolean:IsBaseCurrency'
            };
        }
    });

    //endregion MoneyAttributeMetadata

    //region AttributeMetadataCollection

    function AttributeMetadataCollection () {
        Array.call(this);
    }

    COSA.Util.deriveOverride(Array, AttributeMetadataCollection, {
        deserialize: function ( node ) {
            var attributeMetadataNodes = node.nodes('AttributeMetadata'),
                _this = this;

            attributeMetadataNodes.forEach(function ( attrMetadataNode ) {
                var typeAttr = attrMetadataNode.attr('i:type'),
                    attrType = 'none', value;

                if ( typeof typeAttr != 'undefined' ) {
                    attrType = typeAttr.split(':')[1];
                }

                value = COSA.hasOwnProperty(attrType) ?
                    new COSA[attrType]() :
                    new AttributeMetadata();

                _this.push(value.deserialize(attrMetadataNode));
            });

            return this;
        }
    });

    //endregion AttributeMetadataCollection

    exports.AttributeMetadata = AttributeMetadata;
    exports.StringAttributeMetadata = StringAttributeMetadata;
    exports.LookupAttributeMetadata = LookupAttributeMetadata;
    exports.PicklistAttributeMetadata = PicklistAttributeMetadata;
    exports.BooleanAttributeMetadata = BooleanAttributeMetadata;
    exports.DateTimeAttributeMetadata = DateTimeAttributeMetadata;
    exports.MemoAttributeMetadata = MemoAttributeMetadata;
    exports.ImageAttributeMetadata = ImageAttributeMetadata;
    exports.IntegerAttributeMetadata = IntegerAttributeMetadata;
    exports.DoubleAttributeMetadata = DoubleAttributeMetadata;
    exports.DecimalAttributeMetadata = DecimalAttributeMetadata;
    exports.MoneyAttributeMetadata = MoneyAttributeMetadata;
    exports.AttributeMetadataCollection = AttributeMetadataCollection;

})(COSA);
